#include <lversion.h>
#if L_VERSION_MODULE_TRI_MESH == 111225

#include "_ltriMesh_111225.h"

using namespace llib;
using namespace llib::geom;

#include <boost/assert.hpp>

#include <map>
using std::pair;
using std::map;

Vec3f& llib::geom::ModelEdge::GetVertex( const EdgeVertexName& name ) const
{
    switch (name) {
        case EdgeVertexBegin: return *_vertexBegin;
        case EdgeVertexEnd: return *_vertexEnd;
        default: return *_vertexBegin;
    }
}

Vec3f& llib::geom::ModelEdge::GetVertex( const Int32 name ) const
{
    switch (name) {
        case (Int32)EdgeVertexBegin: return *_vertexBegin;
        case (Int32)EdgeVertexEnd: return *_vertexEnd;
        default: return *_vertexBegin;
    }
}

const Int32 llib::geom::ModelEdge::GetVertexIndex( const EdgeVertexName& name ) const
{
    switch (name) {
        case EdgeVertexBegin: return _vertexIndexBegin;
        case EdgeVertexEnd: return _vertexIndexEnd;
        default: return _vertexIndexBegin;
    }
}

const Int32 llib::geom::ModelEdge::GetVertexIndex( const Int32 name ) const
{
    switch (name) {
        case (Int32)EdgeVertexBegin: return _vertexIndexBegin;
        case (Int32)EdgeVertexEnd: return _vertexIndexEnd;
        default: return _vertexIndexBegin;
    }
}

Vec3f& llib::geom::ModelFace::GetVertex( const FaceVertexName& name ) const
{
    switch (name) {
        case FaceVertexA: return *_vertexA;
        case FaceVertexB: return *_vertexB;
        case FaceVertexC: return *_vertexC;
        default: return *_vertexA;
    }
}

Vec3f& llib::geom::ModelFace::GetVertex( const Int32& name ) const
{
    switch (name) {
        case (Int32)FaceVertexA: return *_vertexA;
        case (Int32)FaceVertexB: return *_vertexB;
        case (Int32)FaceVertexC: return *_vertexC;
        default: return *_vertexA;
    }
}

const Int32 llib::geom::ModelFace::GetVertexIndex( const FaceVertexName& name ) const
{
    switch (name) {
        case FaceVertexA: return _vertexIndexA;
        case FaceVertexB: return _vertexIndexB;
        case FaceVertexC: return _vertexIndexC;
        default: return _vertexIndexA;
    }
}

const Int32 llib::geom::ModelFace::GetVertexIndex( const Int32& name ) const
{
    switch (name) {
        case (Int32)FaceVertexA: return _vertexIndexA;
        case (Int32)FaceVertexB: return _vertexIndexB;
        case (Int32)FaceVertexC: return _vertexIndexC;
        default: return _vertexIndexA;
    }
}

void llib::geom::TriMesh::resetModelData( std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList )
{
    {
    // remove old data when necessary
    if(_modelBuild) {
        delete[] _vertexList;
        delete[] _faceVertexIndexList;
        delete[] _faceEdgeIndexList;
        delete[] _edgeVertexIndexList;
        delete[] _edgeFaceIndexList;
    }
    }

    {
    // copy vertex list
    _vertexNum  = SZ(vertexList);
    _vertexList = new Vec3f[_vertexNum];
    FOR(I,0,_vertexNum) _vertexList[I] = vertexList[I];
    }

    {
    // copy face-vertex index list
    _faceNum    = SZ(faceVertexIndexList);
    _faceVertexIndexList = new Vec3i[_faceNum];
    FOR(I,0,_faceNum) _faceVertexIndexList[I] = faceVertexIndexList[I];
    // and init face-edge index list
    _faceEdgeIndexList = new Vec3i[_faceNum];
    FOR(I,0,_faceNum) FOR(J,0,3) _faceEdgeIndexList[I][J] = InvalidFaceIndex;
    }

    {
    // generate edges and relations
    typedef pair<Int32,Int32>       EdgePair;
    typedef pair<Int32,EdgeType>    FaceInfo;
    typedef pair<FaceInfo,FaceInfo> FacePair;
    const static EdgeType EdgeTypes[3] = { EdgeAB, EdgeBC, EdgeCA };
    const static Int32 VertexIndex[3][2] = { { 0, 1 }, { 1, 2 }, { 2, 0 } };
    // here edgeMap is a collection collects edge info (a little more complicated)
    // EdgePair : (VertexIndexBegin, VertexIndexEnd)    : where VertexIndexBegin < VertexIndexEnd
    // FaceInfo : (FaceIndex, current edge type of current face)
    // FacePair : (FaceIndexLeft, FaceIndexRight)
    // EdgePair -> FacePair <==>
    //     (VertexIndexBegin, VertexIndexEnd) -> ( (FaceIndexLeft, EdgeType of LeftFace), (FaceIndexRight, EdgeType of RightFace) )
    map<EdgePair,FacePair> edgeMap;
    EdgePair tmpKey;
    FacePair tmpVal;
    FOR(F,0,_faceNum) {
        Vec3i& faceVertexIndex = _faceVertexIndexList[F];
        FOR(E,0,3) {
            Int32 vertexIndexBegin  = faceVertexIndex[ VertexIndex[E][0] ];
            Int32 vertexIndexEnd    = faceVertexIndex[ VertexIndex[E][1] ];
            Boolean directionForward = (vertexIndexBegin < vertexIndexEnd);
            tmpKey.first   = vertexIndexBegin;
            tmpKey.second  = vertexIndexEnd;
            if(!directionForward) std::swap(tmpKey.first,tmpKey.second);
            map<EdgePair,FacePair>::iterator iter = edgeMap.find(tmpKey);
            if(iter != edgeMap.end()) {
                tmpVal = iter->second;
            } else {
                tmpVal.first.first  = InvalidFaceIndex;
                tmpVal.second.first = InvalidFaceIndex;
            }
            if(directionForward) {
                tmpVal.first.first  = F;
                tmpVal.first.second = EdgeTypes[E];
            } else {
                tmpVal.second.first = F;
                tmpVal.second.second= EdgeTypes[E];
            }
            edgeMap[ tmpKey ] = tmpVal;
        }
    }
    // edge collection finished
    // generate edge-vertex/edge-face index lists
    _edgeNum = SZ(edgeMap);
    _edgeVertexIndexList = new Vec2i[_edgeNum];
    _edgeFaceIndexList   = new Vec2i[_edgeNum];
    Int32 index = 0;
    for(map<EdgePair,FacePair>::iterator iter = edgeMap.begin();
        iter != edgeMap.end(); ++iter, ++index) {
        Vec2i& edgeVertexIndex  = _edgeVertexIndexList[index];
        Vec2i& edgeFaceIndex    = _edgeFaceIndexList[index];
        const Int32& vertexIndexBegin = iter->first.first;
        const Int32& vertexIndexEnd   = iter->first.second;
        Int32& faceIndexLeft    = iter->second.first.first;
        Int32& faceIndexRight   = iter->second.second.first;
        EdgeType& edgeTypeLeft  = iter->second.first.second;
        EdgeType& edgeTypeRight = iter->second.second.second;

        edgeVertexIndex[0]      = vertexIndexBegin;
        edgeVertexIndex[1]      = vertexIndexEnd;
        edgeFaceIndex[0]        = faceIndexLeft;
        edgeFaceIndex[1]        = faceIndexRight;

        if(faceIndexLeft != InvalidFaceIndex) {
            Vec3i& leftFaceEdgeIndex = _faceEdgeIndexList[faceIndexLeft];
            leftFaceEdgeIndex[Int32(edgeTypeLeft)] = index;
        }
        if(faceIndexRight != InvalidFaceIndex) {
            Vec3i& rightFaceEdgeIndex = _faceEdgeIndexList[faceIndexRight];
            rightFaceEdgeIndex[Int32(edgeTypeRight)] = index;
        }
    }
    }

    // all done, set flag
    _modelBuild = TRUE;
}

void llib::geom::TriMesh::modelCopy( const TriMesh& m )
{
    if(!m._modelBuild) {
        if(_modelBuild) {
            delete[] _vertexList;
            delete[] _faceVertexIndexList;
            delete[] _faceEdgeIndexList;
            delete[] _edgeVertexIndexList;
            delete[] _edgeFaceIndexList;
        }
        _vertexNum = _edgeNum = _faceNum = 0;
        _vertexList = NULL;
        _edgeVertexIndexList = _edgeFaceIndexList = NULL;
        _faceVertexIndexList = _faceEdgeIndexList = NULL;
        _modelBuild = FALSE;
        return;
    }

    // prepare heap memory
    if(_modelBuild) {
        // vertex
        if(_vertexNum != m._vertexNum) {
            _vertexNum = m._vertexNum;
            delete[] _vertexList;
            _vertexList = new Vec3f[_vertexNum];
        }
        // edge
        if(_edgeNum != m._edgeNum) {
            _edgeNum = m._edgeNum;
            delete[] _edgeVertexIndexList;
            delete[] _edgeFaceIndexList;
            _edgeVertexIndexList = new Vec2i[_edgeNum];
            _edgeFaceIndexList = new Vec2i[_edgeNum];
        }
        // face
        if(_faceNum != m._faceNum) {
            _faceNum = m._faceNum;
            delete[] _faceVertexIndexList;
            delete[] _faceEdgeIndexList;
            _faceVertexIndexList = new Vec3i[_faceNum];
            _faceEdgeIndexList = new Vec3i[_faceNum];
        }
    } else {
        _vertexNum              = m._vertexNum;
        _edgeNum                = m._edgeNum;
        _faceNum                = m._faceNum;
        _vertexList             = new Vec3f[_vertexNum];
        _edgeVertexIndexList    = new Vec2i[_edgeNum];
        _edgeFaceIndexList      = new Vec2i[_edgeNum];
        _faceVertexIndexList    = new Vec3i[_faceNum];
        _faceEdgeIndexList      = new Vec3i[_faceNum];
    }

    // deep copy
    FOR(v,0,_vertexNum) {
        _vertexList[v] = m._vertexList[v];
    }
    FOR(e,0,_edgeNum) {
        _edgeVertexIndexList[e] = m._edgeVertexIndexList[e];
        _edgeFaceIndexList[e]   = m._edgeFaceIndexList[e];
    }
    FOR(f,0,_faceNum) {
        _faceVertexIndexList[f] = m._faceVertexIndexList[f];
        _faceEdgeIndexList[f]   = m._faceEdgeIndexList[f];
    }

    // all done, set flag
    _modelBuild = TRUE;
}

llib::geom::TriMesh::TriMesh()
{
    _vertexNum = _edgeNum = _faceNum = 0;
    _vertexList = NULL;
    _edgeVertexIndexList = _edgeFaceIndexList = NULL;
    _faceVertexIndexList = _faceEdgeIndexList = NULL;
    _modelBuild = FALSE;
}

llib::geom::TriMesh::TriMesh( std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList ) : _modelBuild(FALSE)
{
    BOOST_ASSERT(SZ(vertexList) > 0);
    BOOST_ASSERT(SZ(faceVertexIndexList) > 0);
    resetModelData(vertexList, faceVertexIndexList);
}

llib::geom::TriMesh::TriMesh( const TriMesh& m ) : _modelBuild(FALSE)
{
    modelCopy(m);
}

llib::geom::TriMesh::~TriMesh()
{
    delete[] _vertexList;
    delete[] _faceVertexIndexList;
    delete[] _faceEdgeIndexList;
    delete[] _edgeVertexIndexList;
    delete[] _edgeFaceIndexList;
}

llib::geom::TriMesh& llib::geom::TriMesh::operator=( const TriMesh& m )
{
    modelCopy(m);
    return *this;
}

void llib::geom::TriMesh::SetModelData( std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList )
{
    BOOST_ASSERT(SZ(vertexList) > 0);
    BOOST_ASSERT(SZ(faceVertexIndexList) > 0);
    resetModelData(vertexList, faceVertexIndexList);
}

Vec3f& llib::geom::TriMesh::GetVertex( Int32 index )
{
    BOOST_ASSERT(_modelBuild);
    BOOST_ASSERT(index >= 0 && index < _vertexNum);
    return _vertexList[index];
}

ModelEdge llib::geom::TriMesh::GetEdge( Int32 index ) const
{
    BOOST_ASSERT(_modelBuild);
    BOOST_ASSERT(index >=0 && index < _edgeNum);
    Vec2i&  edgeVertexIndex   = _edgeVertexIndexList[index];
    Int32   vertexIndexBegin  = edgeVertexIndex[0];
    Int32   vertexIndexEnd    = edgeVertexIndex[1];
    Vec3f&  vertexBegin       = _vertexList[vertexIndexBegin];
    Vec3f&  vertexEnd         = _vertexList[vertexIndexEnd];

    return ModelEdge(vertexBegin, vertexIndexBegin,
        vertexEnd, vertexIndexEnd, index);
}

ModelEdge llib::geom::TriMesh::GetEdge( ModelFace& face, EdgeType edgeType ) const
{
    BOOST_ASSERT(_modelBuild);
    Int32   faceIndex       = face.GetIndex();
    Vec3i&  faceEdgeIndex   = _faceEdgeIndexList[faceIndex];
    Int32   edgeIndex = -1;
    switch (edgeType) {
        case EdgeAB: edgeIndex = faceEdgeIndex[0]; break;
        case EdgeBC: edgeIndex = faceEdgeIndex[1]; break;
        case EdgeCA: edgeIndex = faceEdgeIndex[2]; break;
    }
    return GetEdge(edgeIndex);
}

ModelFace llib::geom::TriMesh::GetFace( Int32 index ) const
{
    BOOST_ASSERT(_modelBuild);
    BOOST_ASSERT(index >= 0 && index < _faceNum);
    Vec3i&  faceVertexIndex = _faceVertexIndexList[index];
    Int32   vertexIndexA    = faceVertexIndex[0];
    Int32   vertexIndexB    = faceVertexIndex[1];
    Int32   vertexIndexC    = faceVertexIndex[2];
    Vec3f&  vertexA         = _vertexList[vertexIndexA];
    Vec3f&  vertexB         = _vertexList[vertexIndexB];
    Vec3f&  vertexC         = _vertexList[vertexIndexC];

    return ModelFace(vertexA, vertexIndexA, vertexB, vertexIndexB,
        vertexC, vertexIndexC, index);
}

ModelFace llib::geom::TriMesh::GetFace( ModelEdge& edge, FaceType faceType ) const
{
    BOOST_ASSERT(_modelBuild);
    Int32   edgeIndex       = edge.GetIndex();
    Vec2i&  edgeFaceIndex   = _edgeFaceIndexList[edgeIndex];
    Int32   faceIndex = InvalidFaceIndex;
    switch (faceType) {
        case FaceLeft:  faceIndex = edgeFaceIndex[0]; break;
        case FaceRight: faceIndex = edgeFaceIndex[1]; break;
    }
    BOOST_ASSERT(faceIndex != InvalidFaceIndex);
    return GetFace(faceIndex);
}

llib::Boolean llib::geom::TriMesh::IsValidFace( ModelEdge& edge, FaceType faceType ) const
{
    BOOST_ASSERT(_modelBuild);
    Int32   edgeIndex       = edge.GetIndex();
    Vec2i&  edgeFaceIndex   = _edgeFaceIndexList[edgeIndex];
    Int32   faceIndex = InvalidFaceIndex;
    switch (faceType) {
        case FaceLeft:  faceIndex = edgeFaceIndex[0]; break;
        case FaceRight: faceIndex = edgeFaceIndex[1]; break;
    }
    return faceIndex != InvalidFaceIndex;
}

#endif
